iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Software Development

Spring Boot + Kotlin 協程高併發,後端開發新選擇系列 第 22 篇

Day 21:小結,高併發控制的工具箱總覽,準備進實戰

  • 分享至 

  • xImage
  •  

Day 20:實測結果解讀,協程贏在哪裡、代價又是什麼 結尾說得很直接,七天下來累積了節流、逾時取消、背壓、連線池搭配,再加上一套完整的效能量測方法論,五樣工具已經放進手邊的工具箱,接下來該做的是把它們整理成一份完整、能隨時查閱的總覽,而不是讓每一種手段各自散落在不同章節裡。今天就是要做這件事。

六天六個工具,該整理進同一個箱子了

先講清楚今天的定位。這不是新的一天要學新東西,過去六天分別定案的每一個機制,今天都不會重新展開論證,需要複習細節的地方,直接點連結回去看就好。今天要做的事情比較單純:把這六天各自獨立學到的手段,攤開來放在同一張桌上,看看它們彼此的關係。

先用最精簡的方式,把每一個工具各自的核心用途覆盤一次。Day 15:併發限制,用 Semaphore 保護下游別被打爆 定案了 Semaphore 節流,同時執行的協程數量被限制在一個上限內,超過的協程排隊等候,保護的是下游服務不被瞬間湧入的呼叫量打爆。Day 16:逾時與取消,讓卡住的協程別拖垮整個系統 定案了逾時與取消機制,withTimeout 為協程設定執行時間上限,逾時後觸發的取消沿著結構化並發的父子關係往下傳播,解決的是協程異常卡住不放的風險。Day 17:背壓是什麼,生產太快時該怎麼踩煞車 定案了背壓,Backpressure,容量有限的 Channel 讓消費端能夠回頭調節生產端的速度,處理的是生產與消費之間結構性的速度落差。Day 18:協程遇上 R2DBC 連線池,小心連線被偷偷耗盡 定案了協程與 R2DBC 連線池搭配的注意事項,等待連線的行為雖然是暫停而非阻塞,但連線數量本身依然有上限,供需失衡一樣會造成排隊。Day 19:幫協程模型量身打造一場效能比較 與 Day 20:實測結果解讀,協程贏在哪裡、代價又是什麼 這兩天,則是建立了一套以 k6 為基礎的效能量測方法論,讓「協程模型到底帶來多少差異」這個問題有了可以實際驗證的做法,而不是停留在憑直覺判斷。

五個機制加上一套方法論,湊起來就是六件事。過去六天,這六件事是分開學的,每一天專心處理一個問題,這樣的安排在學習階段是合理的,一次塞進太多觀念只會讓人抓不住重點。但學完之後如果沒有做一次整理,這六件事很容易變成六張各自獨立的卡片,遇到實際問題時腦中得從頭翻一遍才能想起來哪個工具對應哪個情境。今天要建立的,是一個「遇到什麼問題該伸手拿哪個工具」的判斷框架,讓這六張卡片變成一份真正好查閱的工具箱,這才是今天真正的任務。

一張問題對照工具箱

判斷框架的建立方式,不依照學習的先後順序排,而是依照問題實際發生時長什麼樣子來分類。遇到問題時,讀者關心的是眼前這個情境屬於哪一種型態,而不是這個工具是系列第幾天教的。

問題型態 對應工具 核心用途
呼叫端同時發起的請求量過多,需要保護下游資源 Semaphore 節流 限制同時執行呼叫下游動作的協程數量,讓下游感受到的壓力可控且穩定
單一協程執行時間過長,需要主動放棄 逾時與取消機制 withTimeout 設定執行時間上限,逾時後沿結構化並發傳播取消
生產與消費速度結構性不對等 背壓,Backpressure 容量有限的 Channel 讓消費端能回頭調節生產端的速度
資料庫連線這類有限資源可能被耗盡 R2DBC 連線池搭配設定 妥善設定連線池大小與逾時取得連線的時間,避免供需失衡拖垮回應時間
不確定某項改動是否真的有效益 k6 效能量測方法論 用實測數據驗證假設,避免只憑情境合理性做判斷

這張表格排的是問題,不是天數,對照著讀者實際遇到的狀況去查,會比翻回某一天的標題快得多。

有一件事必須明確提醒,這五樣工具彼此不互斥,實務上一個真實的高併發服務,往往同時需要好幾種工具搭配運作,而不是挑一個就足夠應付所有情境。Day 15:併發限制,用 Semaphore 保護下游別被打爆 到 Day 18:協程遇上 R2DBC 連線池,小心連線被偷偷耗盡 的範例其實已經示範過這件事,同一段呼叫確認庫存服務的程式碼,先用 withPermit { } 包上 Semaphore 節流限制同時執行的數量,內層再用 withTimeout 包上逾時上限,避免排在前面的呼叫卡住不放而拖累後面等候的協程,兩個工具疊在同一段邏輯上各司其職,互不衝突。這正好呼應 Day 15 到 Day 18 反覆強調的一致性,這些手段全都在結構化並發的框架下運作,取消訊號一路沿著父子結構往下傳播,不管疊了幾層工具,這條規則從頭到尾沒有變過。實務上該怎麼搭配,取決於眼前的問題實際牽涉到表格裡哪幾種型態,可能只需要一種,也可能同時需要三種。

回顧一個重要的邊界,工具箱裡沒有的東西

工具箱整理到這裡,還有一件容易被遺忘的事,值得在正式進入實戰整合期之前再提醒一次。

Day 15:併發限制,用 Semaphore 保護下游別被打爆 當時已經劃清楚一條界線,這裡的工具箱處理的是呼叫端流量控制與資源保護的問題,關注的是協程本身在單一應用程式行程裡的排程行為。但這個工具箱完全沒有涵蓋另一種問題,多個併發請求同時修改同一筆資料時,該如何保證資料本身不會被寫壞。訂單服務扣減庫存正是這種情境的典型例子,這件事發生在資料庫交易層級,跟呼叫端要不要節流是兩個不同維度的問題,Semaphore 再怎麼調低同時執行的數量,也管不到另一台伺服器上同時在跑的另一個協程正在對同一筆庫存做什麼。

這個邊界今天不重新展開完整論證,Day 15 已經講得很清楚,這裡只需要把這個提醒放回工具箱旁邊,避免帶著「工具箱裡的東西已經涵蓋所有併發問題」這種誤解進入接下來的內容。《Day 24:庫存扣減的併發正確性,用資料庫機制取代分散式鎖》 會正式面對這個問題,用資料庫層級的機制處理,不是今天要提前劇透的內容。

工具箱整理好了,接下來要蓋一棟真正的房子

從 Day 1 到今天,這個系列依序走過地基期,把 Thread-per-Request 模型與第一個 suspend function 的心智模型建立起來;核心觀念期,把 Coroutine Scope、Structured Concurrency、Dispatchers、Coroutine Context 這幾個協程運作的骨架搭好;生態整合期,把協程放進 Spring MVC、WebFlux、R2DBC 這些既有生態系裡找到各自的位置;併發深化期,也就是剛剛整理完的這七天,把高併發情境下真正會用到的控制手段一一補齊。累積到這裡,該懂的觀念已經相當完整,是時候把這些觀念收斂進一個真正的專案。

下一篇,《Day 22:收斂進一個具名專案,訂單服務實戰整合版啟動》 會正式啟動這個示範專案,訂單服務實戰整合版,接下來九天,會把今天整理好的整份工具箱,連同前面三個階段建立的所有觀念,實際裝進這個專案裡。

在正式開始之前,有一件事需要提前說清楚,避免下一篇一開頭就讓人措手不及。自 Day 22 起,這個系列不會提供可下載的完整原始碼倉庫。每一天的文章會呈現當天最關鍵的程式碼片段,搭配足夠的說明文字,讀者可以照著這些片段自行組裝成專案,但不會有一個 repo 連結讓人直接把整包程式碼抓下來跑。這樣安排的理由很單純,一旦開始規劃完整的專案目錄結構、處理各種環境設定與依賴版本,內容很容易被這些瑣碎的架設細節拖著走,反而模糊了每天真正該聚焦的觀念與設計決策本身。接下來九天,每篇想留住的重點始終是某個設計決策為什麼這樣下,資料夾該怎麼擺這類專案架設細節不會是討論的主角。

工具箱整理完了,房子的地基也在前四個階段打好了。下一篇,示範專案正式啟動。


上一篇
Day 20:實測結果解讀,協程贏在哪裡、代價又是什麼
下一篇
Day 22:收斂進一個具名專案,訂單服務實戰整合版啟動
系列文
Spring Boot + Kotlin 協程高併發,後端開發新選擇 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言